package com.wn.gateway.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wn.common.result.Result;
import com.wn.common.result.enums.IErrorCode;
import com.wn.common.result.enums.impl.BusinessCode;
import com.wn.common.utils.JwtTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.List;

/**
 *    全局过滤器   在这里 进行 校验
 *      1、GlobalFilter  全局拦截器  、执行 filter  进行 拦截是否方向
 *      2、yaml中配置的 route下面的 filter 是局部拦截器
 */
@Configuration
public class AuthFilter implements GlobalFilter {

    @Autowired
    private IgnorePathConfig ignorePathConfig;

    public static final String AUTHORIZATION = "Authorization";
    public static final String BEARER = "Bearer ";

    @Autowired
    private JwtTemplate jwtTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 这里的api 是 reactor 下面的包中内容   了解就可以
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //01 拿到所有的url 判断 这个url 是否需要 判断token
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //拿到请求的url
        String path = request.getURI().getPath();

        //url校验 校验
        AntPathMatcher pathMatcher = new AntPathMatcher();
        //校验的写法  自己写  url的规则校验    配置白名单
        List<String> whiteList = ignorePathConfig.getWhiteList();

        //anyMatch() 至少包含一个 结果 为boolean
        boolean flag = whiteList.stream().anyMatch(url -> pathMatcher.match(url, path));
        //判断 是否需要校验
        if (flag){
            return chain.filter(exchange);
        }
        System.out.println("需要校验 = ");
        //02 从头信息拿到token
        //authorization Bear token
        String token = request.getHeaders().getFirst(AUTHORIZATION);

        if (StringUtils.hasText(token)){
            //03 token 校验
            try {
                token =  token.replace(BEARER,"");
                boolean verify = jwtTemplate.verify(token);
                if (verify){

                    //这里 和你 生产的 token的时候设置 一定要一样
                    Object object = jwtTemplate.parseToken(token, "userId");
                    String userId = object.toString();

                    //04 判断token是否过期 （去redis 查询就知道是否过期 redis中没有就过期）
                    //登录的时候 存储的token key
                    String key = "TOKEN:" +userId;
                    String redisToken = stringRedisTemplate.opsForValue().get(key);
                    if (StringUtils.isEmpty(redisToken)){
                        try {
                           return responseError(response, BusinessCode.USER_IS_TOKEN_TIMEOUT);
                        } catch (JsonProcessingException ex) {
                            throw new RuntimeException(ex);
                        }
                    }
                    //05 头重写
                    // controller 拿到的事token 每次都要解析token
                    //网关中重写请求头，让请求头携带userId，再转发到微服务，这样在微服务中就可以直接获取登录用户id
                    request.mutate().header("userId",userId);

                    //06 token 的续期 ( redis token刷新 )
                    stringRedisTemplate.expire(key, Duration.ofMinutes(30));

                    return chain.filter(exchange);
                }
            }catch (Exception e){
                try {
                  return   responseError(response, BusinessCode.USER_IS_TOKEN);
                } catch (JsonProcessingException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }else{
            // 抛出异常
            try {
               return responseError(response, BusinessCode.USER_NOT_TOKEN);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        return chain.filter(exchange);
    }


    /**
     * 返回异常  不能直接 抛出 Exception
     * @param response
     * @param resultCode
     * @return
     * @throws JsonProcessingException
     */
    private static Mono<Void> responseError(ServerHttpResponse response, IErrorCode resultCode) throws JsonProcessingException {
        // 返回错误
        Result<Object> result = Result.fail(resultCode);
        //转成json
        byte[] bytes = new ObjectMapper().writeValueAsBytes(result);
        //reactor 的技术 数据返回
        DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(dataBuffer));
    }

    public static void main(String[] args) {

        AntPathMatcher pathMatcher = new AntPathMatcher();
        boolean match = pathMatcher.match("/abc", "/abc");
        boolean match1 = pathMatcher.match("/abc/**", "/abc/a");
        System.out.println("match1 = " + match1);
        
    }
}